//go:build linux
// +build linux

package system

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	_ "net/http/pprof"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/mem"
)

// 获取当前执行程序所在的绝对路径
func AppPath() string {
	exePath, err := os.Executable()
	if err != nil {
		return "./"
	}

	res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))

	return res
}

func GetCpuPercent() float64 {
	percent, _ := cpu.Percent(time.Second, false)
	return percent[0]
}

func GetMemPercent() float64 {
	memInfo, _ := mem.VirtualMemory()
	return memInfo.UsedPercent
}

func GetDiskPercent(p int) float64 {
	parts, _ := disk.Partitions(true)
	diskInfo, _ := disk.Usage(parts[p].Mountpoint)
	return diskInfo.UsedPercent
}

func GetDiskTotal(p int) uint64 {
	parts, _ := disk.Partitions(true)
	diskInfo, _ := disk.Usage(parts[p].Mountpoint)
	return diskInfo.Total
}

func GetDiskFree(p int) uint64 {
	parts, _ := disk.Partitions(true)
	diskInfo, _ := disk.Usage(parts[p].Mountpoint)
	return diskInfo.Free
}

// 取内存信息
func GetMemInfo() map[string]interface{} {
	memInfo, _ := mem.VirtualMemory()

	result := map[string]interface{}{
		"iTotal":   memInfo.Total,
		"iFree":    memInfo.Free,
		"iUsed":    memInfo.Used,
		"fPercent": memInfo.Used,
		"sTotal":   memoryTxt(memInfo.Total),
		"sFree":    memoryTxt(memInfo.Free),
		"sUsed":    memoryTxt(memInfo.Used),
		"sPercent": fmt.Sprintf("%.2f", memInfo.UsedPercent),
	}

	return result
}

// 程序内存信息
func AppMemoryInfo() map[string]interface{} {
	result := map[string]interface{}{}

	var m runtime.MemStats
	runtime.ReadMemStats(&m)

	result["iAppFree"] = (m.Sys - m.HeapAlloc)
	result["iAppTotal"] = m.Sys
	result["iAppUsage"] = m.HeapAlloc
	result["fUsagePercent"] = m.HeapAlloc / m.Sys
	result["sAppFree"] = memoryTxt(m.Sys - m.HeapAlloc)
	result["sAppTotal"] = memoryTxt(m.Sys)
	result["sAppUsage"] = memoryTxt(m.HeapAlloc)
	result["sUsagePercent"] = fmt.Sprintf("%.2f", float64(m.HeapAlloc/m.Sys))

	return result
}

// 取磁盘信息
func GetDiskInfo() []map[string]interface{} {
	result := []map[string]interface{}{}

	parts, _ := disk.Partitions(true)
	for i := 0; i < len(parts); i++ {
		diskInfo, err := disk.Usage(parts[i].Mountpoint)
		if err != nil {
			break
		}

		m := map[string]interface{}{}
		m["iTotal"] = diskInfo.Total
		m["iFree"] = diskInfo.Free
		m["iUsed"] = diskInfo.Used
		m["fPercent"] = diskInfo.UsedPercent
		m["sTotal"] = memoryTxt(diskInfo.Total)
		m["sFree"] = memoryTxt(diskInfo.Free)
		m["sUsed"] = memoryTxt(diskInfo.Used)
		m["sPercent"] = fmt.Sprintf("%.2f", diskInfo.UsedPercent)

		result = append(result, m)
	}

	return result
}

// 存储显示
func memoryTxt(b uint64) string {
	temp := b
	array := []string{"bit", "Byte", "MB", "GB", "TB"}
	for i := 0; i < len(array); i++ {
		if (temp / 1024) < 1 {
			return fmt.Sprintf("%v", temp) + array[i]
		}

		temp = temp / 1024
	}

	return fmt.Sprintf("%vbyte", temp)
}

// 取CPU占用信息
func GetCpuInfo() map[string]interface{} {
	result := map[string]interface{}{}

	percent, _ := cpu.Percent(time.Second, false)

	result["percent"] = percent
	result["count"] = runtime.GOMAXPROCS(0)

	return result
}

// 取系统信息
func GetOsInfo() map[string]interface{} {
	result := map[string]interface{}{}

	name, err := os.Hostname()
	if err == nil {
		result["sName"] = name
	}

	result["sOs"] = runtime.GOOS
	result["sArch"] = runtime.GOARCH //当前的系统架构 architecture

	return result
}

// GetCPUID 获取cpuid
func GetCPUID() string {
	var cpuid string
	cmd := exec.Command("wmic", "cpu", "get", "processorid")

	b, err := cmd.CombinedOutput()

	if err != nil {
		return ""
	}

	cpuid = string(b)
	cpuid = cpuid[12 : len(cpuid)-2]
	cpuid = strings.ReplaceAll(cpuid, "\n", "")

	return strings.TrimSpace(cpuid)
}

// GetBaseBoardID 获取主板的id
func GetBaseBoardID() string {
	var result string
	cmd := exec.Command("wmic", "baseboard", "get", "serialnumber")

	b, err := cmd.CombinedOutput()

	if err != nil {
		return ""
	}

	result = string(b)
	result = result[12 : len(result)-2]
	result = strings.ReplaceAll(result, "\n", "")

	return strings.TrimSpace(result)
}

// 取系统序列号
func GetSystemSerial() string {
	str := GetCPUID()
	result := str[0:4] + "-" + str[4:8] + "-" + str[8:12] + "-" + str[12:16]
	return result
}

/**
 * 取符合本机的注册码
 * @param key 干扰密钥
 * @return
 */
func CreatePollCode(key string) string {
	key = strings.TrimSpace(key)

	var build strings.Builder

	str := GetCPUID()
	data := []byte(str + "_" + key)
	md5Ctx := md5.New()
	md5Ctx.Write(data)
	cipherStr := md5Ctx.Sum(nil)
	result := hex.EncodeToString(cipherStr)

	for i := 0; i < len(result); i++ {
		if i%2 == 0 {
			continue
		}

		ch := result[i]
		build.WriteString(string(ch))
	}

	return strings.ToUpper(build.String())
}

/**
 * 验证注册码
 * @param code 注册码
 * @param key 干扰密钥
 * @return
 */
func CheckPollCode(code, key string) (bool, error) {
	code = strings.TrimSpace(code)
	if code == "" {
		return false, errors.New("序列号为空")
	}

	code = strings.Replace(code, "-", "", -1)
	code = strings.Replace(code, " ", "", -1)

	if len(code) != 16 {
		return false, errors.New("序列号长度错误")
	}

	key = strings.TrimSpace(key)

	var build strings.Builder

	str := GetCPUID()
	data := []byte(str + "_" + key)
	md5Ctx := md5.New()
	md5Ctx.Write(data)
	cipherStr := md5Ctx.Sum(nil)
	result := hex.EncodeToString(cipherStr)

	for i := 0; i < len(result); i++ {
		if i%2 == 0 {
			continue
		}

		ch := result[i]
		build.WriteString(string(ch))
	}

	if !strings.EqualFold(code, build.String()) { //忽略大小写的比较
		return false, errors.New("序列号错误")
	}

	return true, nil
}

/**
 * 根据序列号验证注册码
 * @param code 注册码
 * @param serial 序列号
 * @param key 干扰密钥
 * @return
 */
func CheckPollCodeAndSerial(code, serial, key string) (bool, error) {
	code = strings.TrimSpace(code)
	if code == "" {
		return false, errors.New("注册码为空")
	}

	code = strings.Replace(code, "-", "", -1)
	code = strings.Replace(code, " ", "", -1)

	if len(code) != 16 {
		return false, errors.New("注册码长度错误")
	}

	serial = strings.TrimSpace(serial)
	if serial == "" {
		return false, errors.New("序列号为空")
	}

	serial = strings.Replace(serial, "-", "", -1)
	serial = strings.Replace(serial, " ", "", -1)

	if len(serial) != 16 {
		return false, errors.New("序列号长度错误")
	}

	key = strings.TrimSpace(key)

	var build strings.Builder

	data := []byte(serial + "_" + key)
	md5Ctx := md5.New()
	md5Ctx.Write(data)
	cipherStr := md5Ctx.Sum(nil)
	result := hex.EncodeToString(cipherStr)

	for i := 0; i < len(result); i++ {
		if i%2 == 0 {
			continue
		}

		ch := result[i]
		build.WriteString(string(ch))
	}

	if strings.ToUpper(code) != build.String() {
		return false, errors.New("序列号错误")
	}

	return true, nil
}
